package org.budo.sharding.hint.aop;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.budo.sharding.hint.AnnotationMasterOnlySupport;

import io.shardingsphere.api.HintManager;

/**
 * 通过在方法上添加指定注解，使当前查询走指定的库
 * 
 * @author limingwei
 * 
 * @see org.budo.sharding.hint.annotation.MasterOnly
 * @see org.budo.sharding.hint.mybatis.AnnotationMasterOnlyMybatisPlugin
 */
public class AnnotationRoutingDataSourceAopInterceptor extends AnnotationMasterOnlySupport implements MethodInterceptor {
    public static final ThreadLocal<String> _ROUTING_DATA_SOURCE_THREAD_LOCAL = new ThreadLocal<String>();

    private static final ThreadLocal<HintManager> _HINT_MANAGER_THREAD_LOCAL = new ThreadLocal<HintManager>();

    /**
     * 避免在同一个请求的多个方法的Aop里多次调用HintManager.getInstance();
     */
    private HintManager getHintManager() {
        HintManager hintManager = _HINT_MANAGER_THREAD_LOCAL.get();
        if (null != hintManager) {
            return hintManager;
        }

        // 以线程对象做锁
        Thread currentThread = Thread.currentThread();
        synchronized (currentThread) {
            hintManager = _HINT_MANAGER_THREAD_LOCAL.get();
            if (null != hintManager) {
                return hintManager;
            }

            hintManager = HintManager.getInstance();
            _HINT_MANAGER_THREAD_LOCAL.set(hintManager);
            return hintManager;
        }
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        HintManager hintManager = null;

        String annotatedRoutingDataSource = this.annotatedRoutingDataSource(method);
        if (null != annotatedRoutingDataSource) {
            _ROUTING_DATA_SOURCE_THREAD_LOCAL.set(annotatedRoutingDataSource); // 走指定名称的库
        } else {
            Boolean annotatedMasterOnly = this.annotatedMasterOnly(method);
            Boolean defaultMasterOnly = this.getDefaultMasterOnly();
            if (null == annotatedMasterOnly ? defaultMasterOnly : annotatedMasterOnly) {
                hintManager = this.getHintManager();
                hintManager.setMasterRouteOnly(); // 强制走主库
            }
        }

        // 执行方法
        try {
            return invocation.proceed();
        } finally {
            if (null != hintManager) {
                hintManager.close();
            }

            // 清理
            _HINT_MANAGER_THREAD_LOCAL.remove();
            _ROUTING_DATA_SOURCE_THREAD_LOCAL.remove();
        }
    }
}